/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package yuhuibear.poat.query;

import java.sql.ParameterMetaData;
import java.sql.PreparedStatement;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;

/**
 * 缓存并管理SQL 的参数信息。
 * 
 * @author yuhuibear
 */
public class ParameterTypeBuffer {

	private Map<String, ParamType> setter;

	private SortedSet<ParamType> paramBuf;

	private final int BufSize = 10000;

	private final double BufLoadFactor = 0.7;

	public ParameterTypeBuffer() {
		setter = Collections.synchronizedMap(new HashMap<String, ParamType>());
		paramBuf = Collections.synchronizedSortedSet(new TreeSet<ParamType>(new ParameterMetaDataComparator()));
	}

	/**
	 * 返回对应SQL的 ? 号参数的SQL 类型信息。
	 * 
	 * @param sql
	 * @param pst
	 * @return
	 * @throws java.lang.Exception
	 */
	public int[] getTypes(String sql, PreparedStatement pst) throws Exception {
		int[] types;
		if (setter.containsKey(sql)) {
			types = setter.get(sql).getTypes();
		}
		else {
			ParameterMetaData pmd = pst.getParameterMetaData();
			types = new int[pmd.getParameterCount()];
			for (int i = 0; i < types.length; i++) {
				types[i] = pmd.getParameterType(i + 1);

			}
			ParamType pt = new ParamType(sql, types);
			if (paramBuf.size() > BufSize) {
				/**
				 * 效能???
				 */
				for (int i = paramBuf.size(); i > BufSize * BufLoadFactor; i--) {
					paramBuf.remove(paramBuf.first());
				}
			}
			paramBuf.add(pt);
			setter.put(sql, pt);
		}
		return types;
	}

	private class ParamType implements Comparable<ParamType> {

		private int useCount = 0;

		public String sql;

		private int[] types;

		public ParamType(String str, int[] t) {
			sql = str;
			types = t;
		}

		@Override
		public int compareTo(ParamType o) {
			if (sql.equals(o.sql)) {
				return this.getUseCount() - o.getUseCount();
			}
			else {
				return -1;
			}
		}

		@Override
		public boolean equals(Object o) {
			if (!o.getClass().isAssignableFrom(ParamType.class))
				return false;
			ParamType pb = (ParamType) o;
			if (this.sql.equals(pb.sql)) {
				return true;
			}
			else {
				return false;

			}
		}

		@Override
		public int hashCode() {
			int hash = 5;
			hash = 19 * hash + (this.sql != null ? this.sql.hashCode() : 0);
			return hash;
		}

		/**
		 * @return the useCount
		 */
		public int getUseCount() {
			return useCount;
		}

		/**
		 * @return the types
		 */
		public int[] getTypes() {
			useCount++;
			return types;
		}
	}

	private class ParameterMetaDataComparator implements Comparator<ParamType> {

		@Override
		public int compare(ParamType o1, ParamType o2) {
			return o1.getUseCount() - o2.getUseCount();
		}
	}
}
